#include <aio.h>
#include <errno.h>
#include <aio_misc.h>
#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

struct rtems_aio_notify_func
{
  void (*func) (union sigval);
  union sigval value;
  
};

static void *
rtems_aio_notify_wrapper (void *arg)
{
  struct rtems_aio_notify_func *const nf = arg;
  void (*func) (union sigval) = nf->func;
  union sigval value = nf->value;
  free (nf);
  (*func) (value);
  return NULL;
}

int
rtems_aio_notify_only (struct sigevent *sigev, pthread_t thid)
{
  int result = 0;
  if (sigev->sigev_notify == SIGEV_THREAD)
    {
      pthread_t notify_thd;
      pthread_attr_t attr, *pattr;
      
      pattr = (pthread_attr_t *) sigev->sigev_notify_attributes;
      
      if (pattr == NULL)
	{
	  pthread_attr_init (&attr);
	  pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
	  pattr = &attr;
	}
      
      struct rtems_aio_notify_func *nf = malloc (sizeof *nf);
      if (nf == NULL)
	result = -1;
      else
	{
	  nf->func = sigev->sigev_notify_function;
	  nf->value = sigev->sigev_value;
	  if (pthread_create (&notify_thd, pattr, 
			      rtems_aio_notify_wrapper, nf) < 0)
	    {
	      free (nf);
	      result = -1;
	    }
	}

    }
   else if (sigev->sigev_notify == SIGEV_SIGNAL)   
      result = pthread_kill(thid, sigev->sigev_signo);
        
  return result;

}

void
rtems_aio_notify (rtems_aio_request * req)
{

  rtems_aio_waitlist *waitlist;
  rtems_chain_node *wait_node;
  struct aiocb *aiocbp = req->aiocbp;
  			    
  if (rtems_aio_notify_only (&aiocbp->aio_sigevent,
			     req->caller_thread) != 0)
    {
      aiocbp->error_code = errno;
      aiocbp->return_value = -1;
    }

  wait_node = req->waitlist.first;

  while (!rtems_chain_is_tail (&req->waitlist, wait_node)) {
    waitlist = (rtems_aio_waitlist *) wait_node;
    if (waitlist->sigevp == NULL) {
      if (waitlist->result != NULL && aiocbp->return_value == -1)
        *waitlist->result = -1;

      --*waitlist->counterp;

      pthread_cond_signal (waitlist->cond);

    }
    else
      if (--*waitlist->counterp == 0)
	{
	  rtems_aio_notify_only (waitlist->sigevp, 
				 waitlist->caller_thread);
	  free ((void *) waitlist->counterp);
	}

    wait_node = wait_node->next;

  }

}
